package entities.role;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import entities.concept.AndConcept;
import entities.concept.Concept;

/**
 * A general role path is a sequence of concepts and roles.
 * 
 * @author j55wu
 * 
 */
public class GeneralRolePath {
	private List<Object> sequence;
	// offset ++ when a role is removed, -- when a role is added.
	private int offset;

	public GeneralRolePath() {
		this.sequence = new ArrayList<Object>();
		this.offset = 0;
	}

	public List<Object> getContent() {
		return this.sequence;
	}

	public int getOffset() {
		return this.offset;
	}

	public Object getLastItem() {
		int idx = this.sequence.size();
		if (idx > 0) {
			return this.sequence.get(idx - 1);
		}
		return null;
	}

	public Role getOldestRole() {
		int i = this.getOldestRoleIndex();
		if (i < 0) {
			return null;
		}
		return (Role) this.sequence.get(i);
	}

	public Role getLastRole() {
		int i = this.getLastRoleIndex();
		if (i < 0) {
			return null;
		}
		return (Role) this.sequence.get(i);
	}

	/**
	 * Remove the sublist from the beginning of the list (index=0, inclusive) to
	 * this index(inclusive)
	 * 
	 * @param index
	 * @return A concept generated from the sublist without the ending role
	 */
	public Concept removeContentToIndex(int index) {
		List<Object> removed = this.sequence.subList(0, index + 1);
		// the last one is the role, not to be included in the concept
		Concept c = null;
		switch (removed.size()) {
		case 0:
		case 1:
			c = null;
			break;
		case 2:
			c = (Concept) removed.get(0);
			break;
		default:
			HashSet<Concept> cons = new HashSet<Concept>();
			for (int i = 0; i < removed.size() - 1; i++) {
				cons.add((Concept) removed.get(i));
			}
			c = new AndConcept(cons);
			break;
		}
		removed.clear();
		return c;
	}

	/**
	 * remove contents from the specified index to the end of the list (both
	 * ends inclusive)
	 * 
	 * @param index
	 * @return a concept generated from the index (exclusive) to the end
	 * 
	 */
	public Concept removeContentToBottom(int index) {
		List<Object> removed = this.sequence.subList(index,
				this.sequence.size());
		// the first one is the returned role, not included in the concept
		Concept c = null;
		switch (removed.size()) {
		case 0:
		case 1:
			c = null;
			break;
		case 2:
			c = (Concept) removed.get(1);
			break;
		default:
			HashSet<Concept> cons = new HashSet<Concept>();
			for (int i = 1; i < removed.size(); i++) {
				cons.add((Concept) removed.get(i));
			}
			c = new AndConcept(cons);
			break;
		}
		removed.clear();
		return c;
	}

	public int getLastRoleIndex() {
		for (int i = this.sequence.size() - 1; i >= 0; i--) {
			Object item = this.sequence.get(i);
			if (item instanceof Role) {
				return i;
			}
		}
		return -1;
	}

	public int getOldestRoleIndex() {
		for (int i = 0; i < this.sequence.size(); i++) {
			Object item = this.sequence.get(i);
			if (item instanceof Role) {
				return i;
			}
		}
		return -1;
	}

	public boolean hasRoles() {
		for (Object o : this.sequence) {
			if (o instanceof Role) {
				return true;
			}
		}
		return false;
	}

	public void addContent(Object o) {
		this.sequence.add(o);
		if (o instanceof Role) {
			this.offset--;
		}
	}

	public void rmContent(int index) {
		// remove the specified index of the list
		if (this.sequence.get(index) instanceof Role) {
			this.offset++;
		}
		this.sequence.remove(index);
	}

	public int length() {
		return this.sequence.size();
	}

	public GeneralRolePath newCopy() {
		GeneralRolePath cp = new GeneralRolePath();
		cp.sequence.addAll(this.sequence);
		cp.offset = this.offset;
		return cp;
	}

	@Override
	public String toString() {
		String st = this.offset + "<";
		for (Object o : this.sequence) {
			st += o.toString() + ",";
		}
		return st + ">";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + offset;
		result = prime * result
				+ ((sequence == null) ? 0 : sequence.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof GeneralRolePath))
			return false;
		GeneralRolePath other = (GeneralRolePath) obj;
		if (offset != other.offset)
			return false;
		if (sequence == null) {
			if (other.sequence != null)
				return false;
		} else if (!sequence.equals(other.sequence))
			return false;
		return true;
	}

}
